﻿using Goway.Log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Goway.StackingHelper.Inovance
{
    public class HC_AM600_ModbusTcp
    {
        public enum SoftElemType
        {
            //AM600
            ELEM_QX = 0,     // QX元件
            ELEM_MW = 1,     // MW元件
            ELEM_X = 2,      // X元件(对应QX200~QX300)
            ELEM_Y = 3,      // Y元件(对应QX300~QX400)

            //H3U
            REGI_H3U_Y = 0x20,       // Y元件的定义	
            REGI_H3U_X = 0x21,       // X元件的定义							
            REGI_H3U_S = 0x22,       // S元件的定义				
            REGI_H3U_M = 0x23,       // M元件的定义							
            REGI_H3U_TB = 0x24,      // T位元件的定义				
            REGI_H3U_TW = 0x25,      // T字元件的定义				
            REGI_H3U_CB = 0x26,      // C位元件的定义				
            REGI_H3U_CW = 0x27,      // C字元件的定义				
            REGI_H3U_DW = 0x28,      // D字元件的定义				
            REGI_H3U_CW2 = 0x29,     // C双字元件的定义
            REGI_H3U_SM = 0x2a,      // SM
            REGI_H3U_SD = 0x2b,      //
            REGI_H3U_R = 0x2c,       //
                                     // H5u
            REGI_H5U_Y = 0x30,       // Y元件的定义	
            REGI_H5U_X = 0x31,       // X元件的定义							
            REGI_H5U_S = 0x32,       // S元件的定义				
            REGI_H5U_M = 0x33,       // M元件的定义	
            REGI_H5U_B = 0x34,       // B元件的定义
            REGI_H5U_D = 0x35,       // D字元件的定义
            REGI_H5U_R = 0x36,       // R字元件的定义

        }

        #region 创建与关闭网络连接
        /******************************************************************************
            1.功能描述 :创建网络连接	                  
            2.返 回 值 :TRUE 成功  FALSE 失败
            3.参    数 : sIpAddr:以太网IP地址，
                         nNetId:网络链接编号,用于标记是第几条网络链接,取值范围0~255,默认0 
                         IpPort:以太网端口号,默认502(modbusTcp协议默认端口号为502)
            4.注意事项 : 
        ******************************************************************************/
        [DllImport("StandardModbusApi.dll", EntryPoint = "Init_ETH_String", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool Init_ETH_String(string sIpAddr, int nNetId = 0, int IpPort = 502);

        /******************************************************************************
            1.功能描述 :关闭网络连接                  
            2.返 回 值 :TRUE 成功  FALSE 失败
            3.参    数 : nNetId:网络链接编号,与Init_ETH（）调用的ID一样
            4.注意事项 : 
        ******************************************************************************/
        [DllImport("StandardModbusApi.dll", EntryPoint = "Exit_ETH", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool Exit_ETH(int nNetId = 0);

        #endregion

        #region AM600标准库

        /******************************************************************************
          1.功能描述 : 写Am600软元件
          2.返 回 值 :1 成功  0 失败
          3.参    数 : nNetId:网络链接编号
                       eType：软元件类型    ELEM_QX = 0//QX元件  ELEM_MW = 1 //MW元件
                       nStartAddr:软元件起始地址（QX元件由于带小数点，地址需要乘以10去掉小数点，如QX10.1，
                           请输入101，MW元件直接就是它的元件地址不用处理）
                       nCount：软元件个数
                       pValue：数据缓存区
         4.注意事项 :  1.x和y元件地址需为8进制; 
                       2. 当元件位C元件双字寄存器时，每个寄存器需占4个字节的数据
                       3.如果是写位元件，每个位元件的值存储在一个字节中
         ******************************************************************************/
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount,
            byte[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_Int16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_Int16(SoftElemType eType, int nStartAddr, int nCount,
            short[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_Int32", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_Int32(SoftElemType eType, int nStartAddr, int nCount,
            int[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_UInt16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_UInt16(SoftElemType eType, int nStartAddr, int nCount,
            ushort[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_UInt32", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_UInt32(SoftElemType eType, int nStartAddr, int nCount,
            uint[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_Float", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_Float(SoftElemType eType, int nStartAddr, int nCount,
            float[] pValue, int nNetId = 0);

        /******************************************************************************
         1.功能描述 : 读Am600软元件
         2.返 回 值 :1 成功  0 失败
         3.参    数 : nNetId:网络链接编号
			          eType：软元件类型   ELEM_QX = 0//QX元件  ELEM_MW = 1 //MW元件
			          nStartAddr:软元件起始地址（QX元件由于带小数点，地址需要乘以10去掉小数点，如QX10.1，请输入101，其它元件不用处理）
			          nCount：软元件个数
			          pValue：返回数据缓存区
         4.注意事项 : 如果是读位元件，每个位元件的值存储在一个字节中，pValue数据缓存区字节数必须是8的整数倍
        ******************************************************************************/
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount,
            byte[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_Int16",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_Int16(SoftElemType eType, int nStartAddr, int nCount,
            short[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_Int32",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_Int32(SoftElemType eType, int nStartAddr, int nCount,
            int[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_UInt16",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_UInt16(SoftElemType eType, int nStartAddr, int nCount,
            ushort[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_UInt32",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_UInt32(SoftElemType eType, int nStartAddr, int nCount,
            uint[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_Float",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_Float(SoftElemType eType, int nStartAddr, int nCount,
            float[] pValue, int nNetId = 0);

        #endregion

        #region 已删除
        /*
         public static short[]  ReadInt16(int index ,int readLenth)
         {
             short[] pValue = new short[readLenth];//缓冲区
             int nNetId = 0;
             for (int i = 0; i < readLenth / 100; i++)
             {
                 short[] TValue = new short[100];
                 int nRet = Am600_Read_Soft_Elem_Int16(SoftElemType.ELEM_MW, i * 100+index, 100, TValue, nNetId);
                 if (1 == nRet)
                 { 
                     Array.Copy(TValue, 0, pValue, 100 * i, 100);
                 }
             }

             var lenth = readLenth - 100 * (readLenth / 100);
             var startindex = 100 * (readLenth / 100);
             if (lenth > 0)
             {
                 short[] temp = new short[lenth];
                 var nRet1 = Am600_Read_Soft_Elem_Int16(SoftElemType.ELEM_MW, startindex, lenth, temp, nNetId);
                 if (1 == nRet1)
                 {
                     Array.Copy(temp, 0, pValue, startindex, lenth);
                 }
             }
             return pValue;
         }
        */

        /*
          public static byte[] ReadByte(int index, int readLenth)
          {
              lock (_o)
              {

                  byte[] pValue = new byte[readLenth];//缓冲区
                  int nNetId = 0;
                  for (int i = 0; i < readLenth / 100; i++)
                  {
                      var stratPoint = i * 100 + index;
                      byte[] TValue = new byte[100];
                      int nRet = Am600_Read_Soft_Elem(SoftElemType.ELEM_MW, stratPoint, 100, TValue, nNetId);
                      if (1 == nRet)
                      {
                          Array.Copy(TValue, 0, pValue, 100 * i, 100);
                      }
                  }
                  int lenth = readLenth - 100 * (readLenth / 100) - 9;
                  int startindex = 100 * (readLenth / 100) + 9;
                  if (lenth > 0)
                  {
                      byte[] temp = new byte[lenth];
                      var nRet1 = Am600_Read_Soft_Elem(SoftElemType.ELEM_MW, startindex, lenth, temp, nNetId);
                      if (1 == nRet1)
                      {
                          Array.Copy(temp, 0, pValue, startindex, lenth);
                      }
                  }
                  return pValue;
              }
          }
        */
        #endregion

        private static readonly object _lockObj = new object();

        public static short[] ReadInt16(int index, int readLenth)
        {
            short[] pValue = new short[readLenth];//缓冲区
            try
            {
                lock (_lockObj)
                {
                    short[] TValue = null;
                    short[] temp = null;

                    int nNetId = 0;
                    for (int i = 0; i < readLenth / 100; i++)
                    {
                        var stratPoint = i * 100 + index;
                        TValue = new short[100];
                        int nRet = Am600_Read_Soft_Elem_Int16(SoftElemType.ELEM_MW, stratPoint, 100, TValue, nNetId);
                        if (1 == nRet)
                        {
                            Array.Copy(TValue, 0, pValue, 100 * i, 100);
                            TValue = null;
                        }
                    }
                    int lenth = readLenth - 100 * (readLenth / 100);
                    int startindex = 100 * (readLenth / 100) + index;
                    if (lenth > 0)
                    {
                        temp = new short[lenth];
                        var nRet1 = Am600_Read_Soft_Elem_Int16(SoftElemType.ELEM_MW, startindex, lenth, temp, nNetId);
                        if (1 == nRet1)
                        {
                            Array.Copy(temp, 0, pValue, 100 * (readLenth / 100), lenth);
                            temp = null;
                        }
                    }

                    return pValue;
                }
            }
            catch (Exception ex)
            {
                LoggerHelper.Error(ex);
                return pValue;
            }
        }

        /// <summary>
        /// 写 Int16
        /// </summary>
        /// <param name="index"></param>
        /// <param name="WriteLenth"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public static int WriteInt16(int index, int WriteLenth, short[] pValue)
        {
            short nNetId = 0;
            return Am600_Write_Soft_Elem_Int16(SoftElemType.ELEM_MW, index, WriteLenth, pValue, nNetId);
        }
    }
}